"""
安全配置模块
JWT认证、密码哈希等安全相关功能
"""
from datetime import datetime, timedelta
from typing import Any, Union, Optional
from jose import jwt, JWTError
from passlib.context import CryptContext
from fastapi import HTTPException, status
from app.core.config import settings

# 密码加密上下文
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")

ALGORITHM = "HS256"


def create_access_token(
    subject: Union[str, Any], 
    expires_delta: Optional[timedelta] = None
) -> str:
    """
    创建访问令牌
    Args:
        subject: 令牌主题(通常是用户ID)
        expires_delta: 过期时间增量
    Returns:
        JWT令牌字符串
    """
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(
            minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES
        )
    
    to_encode = {"exp": expire, "sub": str(subject)}
    encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def create_refresh_token(
    subject: Union[str, Any], 
    expires_delta: Optional[timedelta] = None
) -> str:
    """
    创建刷新令牌
    Args:
        subject: 令牌主题(通常是用户ID)
        expires_delta: 过期时间增量
    Returns:
        JWT刷新令牌字符串
    """
    if expires_delta:
        expire = datetime.utcnow() + expires_delta
    else:
        expire = datetime.utcnow() + timedelta(
            minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES
        )
    
    to_encode = {"exp": expire, "sub": str(subject), "type": "refresh"}
    encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=ALGORITHM)
    return encoded_jwt


def verify_token(token: str) -> Optional[str]:
    """
    验证令牌
    Args:
        token: JWT令牌
    Returns:
        用户ID或None
    """
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
        user_id: str = payload.get("sub")
        if user_id is None:
            return None
        return user_id
    except JWTError:
        return None


def verify_refresh_token(token: str) -> Optional[str]:
    """
    验证刷新令牌
    Args:
        token: JWT刷新令牌
    Returns:
        用户ID或None
    """
    try:
        payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[ALGORITHM])
        user_id: str = payload.get("sub")
        token_type: str = payload.get("type")
        
        if user_id is None or token_type != "refresh":
            return None
        
        return user_id
    except JWTError:
        return None


def get_password_hash(password: str) -> str:
    """
    获取密码哈希
    Args:
        password: 明文密码
    Returns:
        密码哈希
    """
    return pwd_context.hash(password)


def verify_password(plain_password: str, hashed_password: str) -> bool:
    """
    验证密码
    Args:
        plain_password: 明文密码
        hashed_password: 哈希密码
    Returns:
        是否匹配
    """
    return pwd_context.verify(plain_password, hashed_password)


class SecurityException(HTTPException):
    """安全异常"""
    
    def __init__(self, detail: str = "安全验证失败"):
        super().__init__(
            status_code=status.HTTP_401_UNAUTHORIZED,
            detail=detail,
            headers={"WWW-Authenticate": "Bearer"},
        )


class TokenExpiredException(SecurityException):
    """令牌过期异常"""
    
    def __init__(self):
        super().__init__(detail="令牌已过期")


class InvalidTokenException(SecurityException):
    """无效令牌异常"""
    
    def __init__(self):
        super().__init__(detail="无效的令牌")


class InsufficientPermissionException(HTTPException):
    """权限不足异常"""
    
    def __init__(self, detail: str = "权限不足"):
        super().__init__(
            status_code=status.HTTP_403_FORBIDDEN,
            detail=detail,
        )


def generate_api_key() -> str:
    """
    生成API密钥
    Returns:
        API密钥字符串
    """
    import secrets
    return secrets.token_urlsafe(32)


def validate_api_key(api_key: str) -> bool:
    """
    验证API密钥
    Args:
        api_key: API密钥
    Returns:
        是否有效
    """
    # 这里可以实现API密钥的验证逻辑
    # 比如从数据库中查询或者缓存中查询
    return len(api_key) == 43  # URL安全的base64编码的32字节字符串长度


def create_token_pair(user_id: int) -> dict:
    """
    创建令牌对(访问令牌和刷新令牌)
    Args:
        user_id: 用户ID
    Returns:
        包含访问令牌和刷新令牌的字典
    """
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
    refresh_token_expires = timedelta(minutes=settings.REFRESH_TOKEN_EXPIRE_MINUTES)
    
    access_token = create_access_token(
        subject=user_id,
        expires_delta=access_token_expires
    )
    
    refresh_token = create_refresh_token(
        subject=user_id,
        expires_delta=refresh_token_expires
    )
    
    return {
        "access_token": access_token,
        "refresh_token": refresh_token,
        "token_type": "bearer",
        "expires_in": settings.ACCESS_TOKEN_EXPIRE_MINUTES * 60  # 转换为秒
    }